# coding=utf-8

import logging
import threading


class EletionManager(object):
    """
    负责处理选举过程
    """

    def __init__(self, zkclient):
        self.zkClient = zkclient
        # 当前进程的角色, 默认为follower, 竞选成功后为master
        self.is_master = False
        self.base_path = None
        self.mynode = None
        self.host_ip = None
        self.elector_path = None
        self.elect_lock = threading.Condition()

    def do_elect(self, base_path, data):
        """
        中心控制器和域控制器的选举过程
        :param base_path: 中心控制器为/election/center, 域控制器为/election/domain
        :param data: 写入节点的内容，一般为master节点的IP地址
        :return:
        """
        try:
            self.base_path = base_path
            self.host_ip = data

            # 建立临时顺序节点，代表自己
            elector_path = base_path + "/candidates"
            self.elector_path = elector_path
            me = self.zkClient.createTempSeqNode(elector_path + "/n-", data)
            self.mynode = me[len(elector_path) + 1:]

            # 竞选过程
            self._elect_master()

            while not self.is_master:
                # 如果不是master，则主线程等待watch触发
                self.elect_lock.acquire()
                self.elect_lock.wait()
                self.elect_lock.release()
        except Exception as e:
            logging.error(e)
            raise

    def _elect_master(self):
        # 查询自己是不是序号最小的
        znodes = self.zkClient.getChildren(self.elector_path)
        min_znode = self._get_min_znode(znodes)

        # 如果是最小，则为master
        if self.mynode == min_znode:
            master_node = self.base_path + "/master"
            self.zkClient.createTempNode(master_node, self.host_ip)
            self.is_master = True
        else:
            # 如果不是最小，则为slave，并且监控前一个小的节点
            last_node = self._get_last_znode(self.mynode, znodes)
            # 在自己前一个节点的地址
            last_node_path = self.elector_path + "/" + last_node
            # 当watch触发后，说明自己可能已经成为leader
            self.zkClient.addWatch(last_node_path, self.leader_watcher)
            self.is_master = False

    @staticmethod
    def _get_min_znode(allnodes):
        """
        找到序号最小的节点
        :return:
        """
        return sorted(allnodes)[0]

    @staticmethod
    def _get_last_znode(mynode, allnodes):
        """
        找到序号比自己小的节点
        :return:
        """
        sortedlist = sorted(allnodes)
        idx = sortedlist.index(mynode) - 1
        return sortedlist[max(idx, 0)]

    def leader_watcher(self, event):
        """
        当master出现问题，则开始重新竞选
        :return:
        """
        # 查询自己是不是序号最小的
        logging.warn("leader watch trigger: %s" % str(event))
        # 重新选举
        self._elect_master()
        logging.warn("is_master: %s" % self.is_master)
        if self.is_master:
            self.elect_lock.acquire()
            self.elect_lock.notifyAll()
            self.elect_lock.release()
